package com.ideaaedi.commonspring.transaction;

import com.ideaaedi.commonds.function.NoArgConsumer;
import com.ideaaedi.commonds.function.NoArgFunction;
import com.ideaaedi.commonds.transaction.ManualTransactionSupport;
import com.ideaaedi.commonspring.lock.DefaultRedisLockSupport;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.function.Consumer;
import java.util.function.Function;

/**
 * 手动事务支持 默认实现
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 2022/5/12 14:52
 */
@Slf4j
public class DefaultManualTransactionSupport implements ManualTransactionSupport {
    
    /**
     * 默认的事务管理器
     */
    private static volatile DataSourceTransactionManager defaultTransactionManager;
    
    /**
     * 事务管理器
     */
    protected DataSourceTransactionManager transactionManager;
    
    /**
     * 事务配置
     */
    protected final TransactionDefinition transactionDefinition;
    
    protected DefaultManualTransactionSupport() {
        this.transactionDefinition = new DefaultTransactionDefinition();
    }
    
    /**
     * @param transactionManager 事务管理器
     */
    protected DefaultManualTransactionSupport(DataSourceTransactionManager transactionManager) {
        this.transactionDefinition = new DefaultTransactionDefinition();
        this.transactionManager = transactionManager;
    }
    
    /**
     * @param transactionName 事务名称
     */
    protected DefaultManualTransactionSupport(String transactionName) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setName(transactionName);
        this.transactionDefinition = def;
    }
    
    protected DefaultManualTransactionSupport(DataSourceTransactionManager transactionManager, String transactionName) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setName(transactionName);
        this.transactionDefinition = def;
        this.transactionManager = transactionManager;
    }
    
    /**
     * @param propagationBehavior 事务传播机制，默认{@link TransactionDefinition#PROPAGATION_REQUIRED}
     */
    protected DefaultManualTransactionSupport(int propagationBehavior) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(propagationBehavior);
        this.transactionDefinition = def;
    }
    
    /**
     * @param transactionName 事务名称
     * @param propagationBehavior 事务传播机制，默认{@link TransactionDefinition#PROPAGATION_REQUIRED}
     */
    protected DefaultManualTransactionSupport(String transactionName, int propagationBehavior) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setName(transactionName);
        def.setPropagationBehavior(propagationBehavior);
        this.transactionDefinition = def;
    }
    
    protected DefaultManualTransactionSupport(DataSourceTransactionManager transactionManager, String transactionName,
                                            int propagationBehavior) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setName(transactionName);
        def.setPropagationBehavior(propagationBehavior);
        this.transactionDefinition = def;
        this.transactionManager = transactionManager;
    }
    
    /**
     * 事务配置
     */
    protected DefaultManualTransactionSupport(TransactionDefinition transactionDefinition) {
        this.transactionDefinition = transactionDefinition;
    }
    
    protected DefaultManualTransactionSupport(DataSourceTransactionManager transactionManager,
                                            TransactionDefinition transactionDefinition) {
        this.transactionDefinition = transactionDefinition;
        this.transactionManager = transactionManager;
    }
    
    @Override
    public <P, R> R exec(Function<P, R> function, P param) {
        DataSourceTransactionManager transactionManager = transactionManager();
        log.debug(" transactionDefinition -> {}", transactionDefinition);
        TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
        try {
            R r = function.apply(param);
            transactionManager.commit(status);
            return r;
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw e;
        }
    }
    
    @Override
    public <R> R exec(NoArgFunction<R> function) {
        DataSourceTransactionManager transactionManager = transactionManager();
        log.debug(" transactionDefinition -> {}", transactionDefinition);
        TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
        try {
            R r = function.apply();
            transactionManager.commit(status);
            return r;
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw e;
        }
    }
    
    @Override
    public <P> void voidExec(Consumer<P> consumer, P param) {
        DataSourceTransactionManager transactionManager = transactionManager();
        log.debug(" transactionDefinition -> {}", transactionDefinition);
        TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
        try {
            consumer.accept(param);
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw e;
        }
    }
    
    @Override
    public void voidExec(NoArgConsumer consumer) {
        DataSourceTransactionManager transactionManager = transactionManager();
        log.debug(" transactionDefinition -> {}", transactionDefinition);
        TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
        try {
            consumer.accept();
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw e;
        }
    }
    
    /**
     * 获取事务管理器
     *
     * @return 事务管理器
     */
    public DataSourceTransactionManager transactionManager() {
        if (this.transactionManager != null) {
            return this.transactionManager;
        }
        if (DefaultManualTransactionSupport.defaultTransactionManager != null) {
            return DefaultManualTransactionSupport.defaultTransactionManager;
        }
        throw new IllegalStateException("There is not transactionManager available.");
    }
    
    /**
     * 初始化默认的 事务管理器
     *
     * @param transactionManager 事务管理器
     */
    public static void initTransactionManager(DataSourceTransactionManager transactionManager) {
        if (DefaultManualTransactionSupport.defaultTransactionManager != null && !DefaultManualTransactionSupport.defaultTransactionManager.equals(transactionManager)) {
            throw new IllegalStateException("defaultTransactionManager already been initialized.");
        }
        synchronized (DefaultRedisLockSupport.class) {
            if (DefaultManualTransactionSupport.defaultTransactionManager != null) {
                if (DefaultManualTransactionSupport.defaultTransactionManager.equals(transactionManager)) {
                    return;
                }
                throw new IllegalStateException("defaultTransactionManager already been initialized.");
            }
            DefaultManualTransactionSupport.defaultTransactionManager = transactionManager;
        }
    }
    
    public static DefaultManualTransactionSupport create() {
        return new DefaultManualTransactionSupport();
    }
    
    /**
     * @param transactionManager 事务管理器
     */
    public static DefaultManualTransactionSupport create(DataSourceTransactionManager transactionManager) {
        return new DefaultManualTransactionSupport(transactionManager);
    }
    
    /**
     * @param transactionName 事务名称
     */
    public static DefaultManualTransactionSupport create(String transactionName) {
        return new DefaultManualTransactionSupport(transactionName);
    }
    
    public static DefaultManualTransactionSupport create(DataSourceTransactionManager transactionManager,
                                                         String transactionName) {
        return new DefaultManualTransactionSupport(transactionManager, transactionName);
    }
    
    /**
     * @param propagationBehavior 事务传播机制，默认{@link TransactionDefinition#PROPAGATION_REQUIRED}
     */
    public static DefaultManualTransactionSupport create(int propagationBehavior) {
        return new DefaultManualTransactionSupport(propagationBehavior);
    }
    
    /**
     * @param transactionName 事务名称
     * @param propagationBehavior 事务传播机制，默认{@link TransactionDefinition#PROPAGATION_REQUIRED}
     */
    public static DefaultManualTransactionSupport create(String transactionName, int propagationBehavior) {
        return new DefaultManualTransactionSupport(transactionName, propagationBehavior);
    }
    
    public static DefaultManualTransactionSupport create(DataSourceTransactionManager transactionManager,
                                                         String transactionName, int propagationBehavior) {
        return new DefaultManualTransactionSupport(transactionManager, transactionName, propagationBehavior);
    }
    
    /**
     * 事务配置
     */
    public static DefaultManualTransactionSupport create(TransactionDefinition transactionDefinition) {
        return new DefaultManualTransactionSupport(transactionDefinition);
    }
    
    public static DefaultManualTransactionSupport create(DataSourceTransactionManager transactionManager,
                                                         TransactionDefinition transactionDefinition) {
        return new DefaultManualTransactionSupport(transactionManager, transactionDefinition);
    }
}
